import bpy
import bmesh
from typing import Union, Callable
from bpy.types import Object
from .get_common_vars import get_common_vars


def enter_object_mode(context) -> None:
    if context.mode != 'OBJECT':
        active_ob = context.active_object
        if active_ob is not None:
            if active_ob.type == 'MESH':
                bpy.ops.object.mode_set(mode='OBJECT', toggle=False)
            else:
                print("Is necessary the active object is of the MESH type for enter in object mode!")
        else:
            print("[enter_object_mode] Is necessary the active object for enter in object mode!")


def enter_edit_mode(context) -> None:
    if context.mode != 'EDIT':
        active_ob = context.active_object
        if active_ob is not None:
            if active_ob.type == 'MESH':
                bpy.ops.object.mode_set(mode='EDIT', toggle=False)
            else:
                print("Is necessary the active object is of the MESH type for enter in edit mode!")
        else:
            print("[enter_edit_mode] Is necessary the active object for enter in edit mode!")


def enter_weight_paint_mode(context) -> None:
    if context.mode != 'WEIGHT_PAINT':
        active_ob = context.active_object
        if active_ob is not None:
            if active_ob.type == 'MESH':
                bpy.ops.object.mode_set(mode='WEIGHT_PAINT', toggle=False)
            else:
                print("Is necessary the active object is of the MESH type for enter in weight paint mode!")
        else:
            print("[enter_weight_paint_mode] Is necessary the active object for enter in weight paint mode!")


def select_object(context, ob_input: Union[str, Object], with_print: bool = False) -> None:
    view_layer, scn = get_common_vars(context, get_view_layer=True, get_scn=True)

    ob = None
    if ob_input:

        if isinstance(ob_input, str):
            if ob_input in view_layer.objects and ob_input in scn.objects:
                ob = scn.objects.get(ob_input)

        elif isinstance(ob_input, Object):
            if ob_input.name in view_layer.objects and ob_input.name in scn.objects:
                ob = ob_input

    if ob is not None:
        if ob.visible_get():
            ob.select_set(True)
        else:
            if with_print:
                print(ob.name + " is not visible in viewport!, not selecting.")


def deselect_object(ob):
    ob.select_set(False)


def deselect_all_no_meshes_objects(context):
    for ob in context.selected_objects:
        if ob.type != 'MESH':
            deselect_object(ob)


def set_active_object(context, ob):

    view_layer = get_common_vars(context, get_view_layer=True)
    
    if not isinstance(ob, Object):
        if isinstance(ob, str):
            if ob in view_layer.objects:
                ob = view_layer.objects.get(ob)
            else:
                ob = False
                print(ob, "not avalidable in bpy.context.scene.objects!")
    if ob:
        if ob.name in view_layer.objects:
            view_layer.objects.active = ob


def deselect_all_objects(context):
    enter_object_mode(context)
    bpy.ops.object.select_all(action='DESELECT')


def select_all_vertices(context):
    ob = context.object

    if ob:
        if ob.type == 'MESH':
            me = ob.data
            bm = bmesh.from_edit_mesh(me)

            for v in bm.verts:
                v.select_set(True)

            bmesh.update_edit_mesh(me)


def get_object_from_data(context, name):
    view_layer = get_common_vars(context, get_view_layer=True)
    return view_layer.objects.get(name, None)


def rm_ob(context, target: Union[str, Object]) -> None:
    view_layer = get_common_vars(context, get_view_layer=True)
    ob = None
    if isinstance(target, str):
        ob = get_object_from_data(target)
    elif isinstance(target, Object):
        ob = target
    else:
        print("[rm_ob]: not valid type obj recived!")
    if ob:
        if ob.name in view_layer.objects:
            bpy.data.objects.remove(ob, do_unlink=True)
        else:
            print("[rm_ob]:", ob, "Not in bpy.data.objects, cant remove object!")
    else:
        print("[rm_obj:", ob, "Not valid object, cant remove object!")


def ocultar_post_panel_settings():
    # ocultar ventana modal de dialogo de properties tras ejecutar un operador:
    bpy.ops.wm.redraw_timer(type='DRAW_WIN_SWAP', iterations=1)


def context_override(context, area_type: str, callback: Callable) -> None:
    
    """ Context Override Blender 4.0 """

    area = next((a for a in context.screen.areas if a.type == area_type), None)
    if area:
        
        # Encuentra una región adecuada dentro del área especificada
        region = next((r for r in area.regions if r.type == 'WINDOW'), None)
        if region:
            override_context = context.copy()
            override_context['window'] = context.window
            override_context['screen'] = context.screen
            override_context['area'] = area
            override_context['region'] = region
            
            with context.temp_override(**override_context):
                callback(context)  # Pasamos el contexto sobrescrito
            
            # with context.temp_override(window=context.window, area=area, region=region):
            #    callback(area)


def append_function_unique(fn_list, fn):

    """ 
        Appending 'fn' to 'fn_list',
        Remove any functions from with a matching name & module.
    """
    
    fn_name = fn.__name__ 
    fn_module = fn.__module__
    for i in range(len(fn_list) - 1, -1, -1):
        if fn_list[i].__name__ == fn_name and fn_list[i].__module__ == fn_module:
            del fn_list[i]
    fn_list.append(fn)